---
id: oauth2-grant-type-jwt-bearer
title: JSON Web Token (JWT) Profile (RFC7523)
---

Ory Hydra is capable of performing the
[JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants](https://tools.ietf.org/html/rfc7523).
This guide defines how a JWT Bearer Token can be used to request an access token
when a client wishes to utilize an existing trust relationship, expressed
through the semantics of the JWT, without a direct user-approval step at the
authorization server (Hydra).

Ory Hydra supports both methods expressed in RFC 7523:

- _Using JWTs as Authorization Grants_: Allows exchanging a JSON Web Token for
  an Access Token.
- _Using JWTs for Client Authentication_: Allows OAuth 2.0 Client Authentication
  using public/private keys via JSON Web Tokens.

## Exchanging JWTs for Access Tokens

To use the Authorization Grant `urn:ietf:params:oauth:grant-type:jwt-bearer`,
the client performs an OAuth 2.0 Access Token Request as defined in
[Section 4.1 of the OAuth Assertion Framework RFC7521](https://datatracker.ietf.org/doc/html/rfc7521#section-4.1)
with the following specific parameter values and encodings:

- The value of the `grant_type` is
  `urn:ietf:params:oauth:grant-type:jwt-bearer`.
- The value of the `assertion` parameter MUST contain a single JWT.

The `scope` parameter may be used, as defined in the OAuth Assertion Framework
[RFC7521](https://datatracker.ietf.org/doc/html/rfc7521), to indicate the
requested scope:

```
POST /oauth2/token HTTP/1.1
Host: public.hydra.com
Content-Type: application/x-www-form-urlencoded

grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer
&assertion=eyJhbGciOiJFUzI1NiIsImtpZCI6IjE2In0.
eyJpc3Mi[...omitted for brevity...].
J9l-ZhwP[...omitted for brevity...]
```

Clients using this grant must be authenticated.

### Establishing a Trust Relationship

Before using this grant type, you must establish a trust relationship in Ory
Hydra. This involves registering the issuer, subject, and the public key at Ory
Hydra's Admin Endpoint:

```
POST https://<admin.ory-hydra>/trust/grants/jwt-bearer/issuers
Content-Type: application/json

{
  // The issuer you want to trust.
  "issuer": "https://my-issuer.com",

  // The "sub" field of the access token to be created.
  "subject": "alice@example.org",

  // The allowed scope of the generated access token.
  "scope": ["read"],

  // The public key with which the JWT Bearer's signature can be verified.
  "jwk": {
    "kty":"RSA",
    "e":"AQAB",
    "kid":"d8e91f55-67e0-4e56-a066-6a5f0c2efdf7",
    "n":"nzyis1ZjfNB0bBgKFMSvvkTtwlvBsaJq7S5wA-kzeVOVpVWwkWdVha4s38XM_pa_yr47av7-z3VTmvDRyAHcaT92whREFpLv9cj5lTeJSibyr_Mrm_YtjCZVWgaOYIhwrXwKLqPr_11inWsAkfIytvHWTxZYEcXLgAXFuUuaS3uF9gEiNQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0e-lf4s4OxQawWD79J9_5d3Ry0vbV3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWbV6L11BWkpzGXSW4Hv43qa-GSYOD2QU68Mb59oSk2OB-BtOLpJofmbGEGgvmwyCI9Mw"
  }

  // When this trust relationship expires.
  "expires_at": "2021-04-23T18:25:43.511Z",
}
```

The above example would allow the following JWT Bearer

```
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL215LWlzc3Vlci5jb20iLCJzdWIiOiJhbGljZUBleGFtcGxlLm9yZyIsImF1ZCI6Imh0dHBzOi8vcHVibGljLmh5ZHJhLmNvbS9vYXV0aDIvdG9rZW4iLCJuYmYiOjEzMDA4MTU3ODAsImV4cCI6MTMwMDgxOTM4MH0.baBiLrVRRU1AKYvAn0X4eIeLvFfpe2wsoD3VTMtaYdbEW2-w-SFeGeEzl5B6sh612bfKkoeihhFVx2md7DP-Rl5asicJzeIhcPETzZbVSPxR1lFdOBwcIPG5N70aSJs2zSn3jnRIhpZf85YZOI8RbQ93Kxla741_4xruHbsNRFqIuWVhxk95BCCnoXzEd8vBTxd_GMn9VijUY_piLPMo-OifRF9pSjYo38aJmRW1tJzeFCMruc9X1W-2c-L_t3rV7zYBH3LlpDZfwyy3T5Pmqf6QKeq1N-MjLnIJcZGT89jqxLmqVFRvAiEyA6iMQXVxmENOnwylGPwuR8DewhWMqg
```

which has the claims

```json5
{
  iss: 'https://my-issuer.com',
  sub: 'alice@example.org',
  aud: 'https://public.hydra.com/oauth2/token',
  nbf: 1300815780,
  exp: 1300819380
}
```

to be exchanged for an OAuth2 Access Token (the `scope` parameter is optional!)

```
POST /oauth2/token HTTP/1.1
Host: public.hydra.com
Content-Type: application/x-www-form-urlencoded

grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer
&scope=read
&assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL215LWlzc3Vlci5jb20iLCJzdWIiOiJhbGljZUBleGFtcGxlLm9yZyIsImF1ZCI6Imh0dHBzOi8vcHVibGljLmh5ZHJhLmNvbS9vYXV0aDIvdG9rZW4iLCJuYmYiOjEzMDA4MTU3ODAsImV4cCI6MTMwMDgxOTM4MH0.baBiLrVRRU1AKYvAn0X4eIeLvFfpe2wsoD3VTMtaYdbEW2-w-SFeGeEzl5B6sh612bfKkoeihhFVx2md7DP-Rl5asicJzeIhcPETzZbVSPxR1lFdOBwcIPG5N70aSJs2zSn3jnRIhpZf85YZOI8RbQ93Kxla741_4xruHbsNRFqIuWVhxk95BCCnoXzEd8vBTxd_GMn9VijUY_piLPMo-OifRF9pSjYo38aJmRW1tJzeFCMruc9X1W-2c-L_t3rV7zYBH3LlpDZfwyy3T5Pmqf6QKeq1N-MjLnIJcZGT89jqxLmqVFRvAiEyA6iMQXVxmENOnwylGPwuR8DewhWMqg
```

with resulting access token claims:

```
{
  "iss": "https://public.hydra.com/",
  "sub": "alice@example.org",
  "scp": ["read"],
  // ...
}
```

You can also delete, get, and list trust relationships. Please check the
[HTTP REST API documentation](../reference/api.mdx) for more details.

### OAuth2 JWT Bearer Grant Type Validation

When performing the `urn:ietf:params:oauth:grant-type:jwt-bearer` Authorization
Grant, the JWT Bearer in the `assertion` parameter is validated as follows:

1. The JWT MUST contain an `iss` (issuer) claim that contains a unique
   identifier for the entity that issued the JWT. The value must match the
   `issuer` value of the trust relationship.
2. The JWT MUST contain a `sub` (subject) claim identifying the principal that
   is the subject of the JWT (e.g. user ID). The value must match the `subject`
   value of the trust relationship.
3. The JWT MUST contain an `aud` (audience) claim containing a value that
   identifies the authorization server (Hydra) as an intended audience. So this
   value must be Hydra Token URL.
4. The JWT MUST contain an `exp` (expiration time) claim that limits the time
   window during which the JWT can be used. Can be controlled by
   `oauth2.grant.jwt.max_ttl` setting.
5. The JWT MAY contain an `nbf` (not before) claim that identifies the time
   before which the token MUST NOT be accepted for processing by Hydra.
6. The JWT MAY contain an `iat` (issued at) claim that identifies the time at
   which the JWT was issued. Controlled by `oauth2.grant.jwt.iat_optional`
   (default `false`) If `iat` is not passed, then current time (when assertion
   is received by Hydra) will be considered as issued date.
7. The JWT MAY contain a `jti` (JWT ID) claim that provides a unique identifier
   for the token. Controlled by `oauth2.grant.jwt.jti_optional` (default
   `false`) setting. **Note**: If `jti` is configured to be required, then Hydra
   will reject all assertions with the same `jti`, if `jti` was already used by
   some assertion, and this assertion is not expired yet (see `exp` claim).
8. The JWT MUST be digitally signed.

If a scope was included in the OAuth2 Access Token Request

```
POST /oauth2/token HTTP/1.1
Host: public.hydra.com
Content-Type: application/x-www-form-urlencoded

grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer
&scope=read
&assertion=...
```

Hydra will check them against scopes defined in the corresponding trust
relationship.

## Using JWTs for Client Authentication

Ory Hydra supports OAuth 2.0 Client Authentication with RSA and ECDSA
private/public keypairs with currently supported signing algorithms:

- RS256 (default), RS384, RS512
- PS256, PS384, PS512
- ES256, ES384, ES512
- EdDSA

This authentication method replaces the classic HTTP Basic Authorization and
HTTP POST Authorization schemes. Instead of sending the `client_id` and
`client_secret`, you authenticate the client with a signed JSON Web Token.

To enable this feature for a specific OAuth 2.0 Client, you must set
`token_endpoint_auth_method` to `private_key_jwt` and register the public key of
the RSA/ECDSA signing key either using the `jwks_uri` or `jwks` fields of the
client.

When authenticating the client at the token endpoint, you generate and sign
(with the RSA/ECDSA private key) a JSON Web Token with the following claims:

- `iss`: REQUIRED. Issuer. This MUST contain the client_id of the OAuth Client.
- `sub`: REQUIRED. Subject. This MUST contain the client_id of the OAuth Client.
- `aud`: REQUIRED. Audience. The aud (audience) Claim. Value that identifies the
  Authorization Server (Ory Hydra) as an intended audience. The Authorization
  Server MUST verify that it is an intended audience for the token. The Audience
  SHOULD be the URL of the Authorization Server's Token Endpoint.
- `jti`: REQUIRED. JWT ID. A unique identifier for the token, which can be used
  to prevent reuse of the token. These tokens MUST only be used once, unless
  conditions for reuse were negotiated between the parties; any such negotiation
  is beyond the scope of this specification.
- `exp`: REQUIRED. Expiration time on or after which the ID Token MUST NOT be
  accepted for processing.
- `iat`: OPTIONAL. Time at which the JWT was issued.

When making a request to the `/oauth2/token` endpoint, you include
`client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer`
and `client_assertion=<signed-jwt>` in the request body:

```
POST /oauth2/token HTTP/1.1
Host: my-hydra.com
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&
code=i1WsRn1uB1&
client_id=s6BhdRkqt3&
client_assertion_type=
urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&
client_assertion=PHNhbWxwOl ... ZT
```

Here's what a client with a `jwks` containing one RSA public key looks like:

```json
{
  "client_id": "rsa-client-jwks",
  "jwks": {
    "keys": [
      {
        "kty": "RSA",
        "n": "jL7h5wc-yeMUsHGJHc0xe9SbTdaLKXMHvcIHQck20Ji7SvrHPdTDQTvZtTDS_wJYbeShcCrliHvbJRSZhtEe0mPJpyWg3O_HkKy6_SyHepLK-_BR7HfcXYB6pVJCG3BW-lVMY7gl5sULFA74kNZH50h8hdmyWC9JgOHn0n3YLdaxSWlhctuwNPSwqwzY4qtN7_CZub81SXWpKiwj4UpyB10b8rM8qn35FS1hfsaFCVi0gQpd4vFDgFyqqpmiwq8oMr8RZ2mf0NMKCP3RXnMhy9Yq8O7lgG2t6g1g9noWbzZDUZNc54tv4WGFJ_rJZRz0jE_GR6v5sdqsDTdjFquPlQ",
        "e": "AQAB",
        "use": "sig",
        "kid": "rsa-jwk"
      }
    ]
  },
  "token_endpoint_auth_method": "private_key_jwt",
  "token_endpoint_auth_signing_alg": "RS256"
}
```

And here is how it looks like for a `jwks` including an ECDSA public key:

```json
{
  "client_id": "ecdsa-client-jwks",
  "jwks": {
    "keys": [
      {
        "kty": "EC",
        "use": "sig",
        "crv": "P-256",
        "kid": "ecdsa-jwk",
        "x": "nQjdhpecjZRlworpYk_TJAQBe4QbS8IwHY1DWkfR0w0",
        "y": "UQfLzHxhc4i3EETUeaAS1vDVFJ-Y01hIESiXqqS86Vc"
      }
    ]
  },
  "token_endpoint_auth_method": "private_key_jwt",
  "token_endpoint_auth_signing_alg": "ES256"
}
```

And here is how it looks like for a `jwks` including an EdDSA public key:

```json
{
  "client_id": "eddsa-client-jwks",
  "jwks": {
    "keys": [
      {
        "kty": "OKP",
        "use": "sig",
        "crv": "Ed25519",
        "kid": "eddsa-jwk",
        "x": "cg1qGqQGSF6xvzoDZVaDfxu0c2fPhUEuVHYUr1WYVXs"
      }
    ]
  },
  "token_endpoint_auth_method": "private_key_jwt",
  "token_endpoint_auth_signing_alg": "EdDSA"
}
```

And with `jwks_uri`:

```json
{
  "client_id": "client-jwks-uri",
  "jwks_uri": "http://path-to-my-public/keys.json",
  "token_endpoint_auth_method": "private_key_jwt",
  "token_endpoint_auth_signing_alg": "RS256"
}
```

The `jwks_uri` must return a JSON object containing the public keys associated
with the OAuth 2.0 Client:

```json
{
  "keys": [
    {
      "kty": "RSA",
      "n": "jL7h5wc-yeMUsHGJHc0xe9SbTdaLKXMHvcIHQck20Ji7SvrHPdTDQTvZtTDS_wJYbeShcCrliHvbJRSZhtEe0mPJpyWg3O_HkKy6_SyHepLK-_BR7HfcXYB6pVJCG3BW-lVMY7gl5sULFA74kNZH50h8hdmyWC9JgOHn0n3YLdaxSWlhctuwNPSwqwzY4qtN7_CZub81SXWpKiwj4UpyB10b8rM8qn35FS1hfsaFCVi0gQpd4vFDgFyqqpmiwq8oMr8RZ2mf0NMKCP3RXnMhy9Yq8O7lgG2t6g1g9noWbzZDUZNc54tv4WGFJ_rJZRz0jE_GR6v5sdqsDTdjFquPlQ",
      "e": "AQAB",
      "use": "sig",
      "kid": "rsa-jwk"
    }
  ]
}
```
